package com.twotwoshop.service.search.service.Impl;



import cn.hutool.json.JSONUtil;

import com.twotwoshop.commom.es.EsConstants;
import com.twotwoshop.domin.common.dtos.PageResponseResult;
import com.twotwoshop.domin.common.dtos.ResponseResult;
import com.twotwoshop.domin.common.enums.AppHttpCodeEnum;
import com.twotwoshop.domin.goods.dtos.GoodsSearchDto;
import com.twotwoshop.domin.goods.pojo.Product;
import com.twotwoshop.domin.goods.vos.ProductVo;
import com.twotwoshop.domin.search.ApUserSearch;
import com.twotwoshop.domin.user.pojos.ApUser;
import com.twotwoshop.fegin.api.user.IUserClient;
import com.twotwoshop.service.search.service.GoodsHistoryService;
import com.twotwoshop.service.search.service.GoodsSearchService;
import com.twotwoshop.service.search.strategy.SearchImpl.GoodsSearchFactory;
import com.twotwoshop.service.search.strategy.SearchGranter;
import com.twotwoshop.util.thread.AppThreadLocalUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;


@Service
@Slf4j
@RequiredArgsConstructor
public class GoodsSearchServiceImpl implements GoodsSearchService {


    //MongoDb
    private final MongoTemplate mongoTemplate;

    //es
    private final RestHighLevelClient client;


    private final IUserClient userClient;

    @Autowired
    private GoodsHistoryService goodsHistoryService;



    /**
     * es文章分页检索
     *
     * @param goodsSearchDto
     * @return
     */
    @Override
    public ResponseResult search(GoodsSearchDto goodsSearchDto) {

        //1.检查参数
        goodsSearchDto.checkParam();

        //异步保存搜索记录
        goodsHistoryService.insert(goodsSearchDto.getKeyword(), AppThreadLocalUtil.getUser().getId());


        // 1.创建request对象
        SearchRequest request = new SearchRequest(EsConstants.ES_PRODUCTS);

        // 1.创建集合存储
        List<ProductVo> goodsList = new ArrayList<>();

        // 1.1 构建查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 创建布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();


        // 搜索参数不为空
        if (goodsSearchDto.getKeyword() != null && !goodsSearchDto.getKeyword().isEmpty()) {
            boolQueryBuilder.must(QueryBuilders.multiMatchQuery(goodsSearchDto.getKeyword(),
                    "title",          // 标题
                    "description"     ));        // 描述
        }

        // 类型参数不为空
        if (goodsSearchDto.getCategoryId()!=null) {
            // 添加精确匹配的查询条件，针对categoryId字段
            boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId", goodsSearchDto.getCategoryId()));
        }


        //查询上架的商品
        // 添加status等于1的查询条件
        boolQueryBuilder.must(QueryBuilders.termQuery("status", 1));


        // 将布尔查询设置到searchSourceBuilder
        searchSourceBuilder.query(boolQueryBuilder);

        // 按照创建时间倒叙查询
        searchSourceBuilder.sort("createdTime", SortOrder.DESC);


        // 分页查询  因为Elasticsearch的分页是从0开始的。
        int from = (goodsSearchDto.getPage() - 1) * goodsSearchDto.getSize();
        searchSourceBuilder.from(from).size(goodsSearchDto.getSize());

        //3.0 准备请求
        request.source(searchSourceBuilder);
        //3.1 发送请求 得到相应结果
        SearchResponse response = null;

        try {
            response = client.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        //4.解析结果
        SearchHits searchHits = response.getHits();


        //4.1 查询总条数
        long total = searchHits.getTotalHits().value;
        if (total == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"没有商品");
        }

        //4.2 查询的结果数组
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //4.3 得到source
            String json = hit.getSourceAsString();

            //将返回的json转为Goods对象
            Product goods = JSONUtil.parseObj(json).toBean(Product.class);

            ProductVo productVo = new ProductVo();
            BeanUtils.copyProperties(goods, productVo);

            //将图片路径转化为集合
            List<String> imageList = Arrays.stream(goods.getImages().split(","))
                    .map(String::trim)
                    .collect(Collectors.toList());

            productVo.setImages(imageList);

            //查询发布商品人的昵称和头像
            ResponseResult userInfo = userClient.getUserInfoById(goods.getUserId());
            if (userInfo.getCode()==200){
                //拿取用户信息
                String jsonString = JSONUtil.toJsonStr(userInfo.getData());
                ApUser user = JSONUtil.toBean(jsonString, ApUser.class);

                //设置用户的名称和头像
                productVo.setName(user.getName());
                productVo.setImage(user.getImage());
                productVo.setUserId(user.getId().longValue());
            }

            goodsList.add(productVo);

        }


        //3.结果返回
        ResponseResult responseResult = new PageResponseResult(goodsSearchDto.getPage(), goodsSearchDto.getSize(), (int) total);
        responseResult.setData(goodsList);

        return responseResult;


    }



    /**
     * 查询时记录搜索历史 保存用户搜索历史
     *
     * @param keyword
     * @param userId
     */
    @Override
    @Async("taskExecutor")
    public void insert(String keyword, Integer userId) {

        log.info("保存用户搜索记录");

        //参数判空 用户搜索词为空 什么都不做
        if (StringUtils.isBlank(keyword) || userId==null) {
            return;
        }

        //1.查询用户搜索历史记录
        //封装查询条件Query
        Query query = new Query(Criteria.where("userId").is(userId).and("keyword").is(keyword));

        ApUserSearch apUserSearch = mongoTemplate.findOne(query, ApUserSearch.class);

        //2.存在，更新创建时间
        if (apUserSearch!=null) {
            apUserSearch.setCreatedTime(new Date());
            mongoTemplate.save(apUserSearch);
            return;
        }

        //3.不存在 添加新的搜索记录
        apUserSearch = new ApUserSearch();
        apUserSearch.setUserId(userId);
        apUserSearch.setKeyword(keyword);
        apUserSearch.setCreatedTime(new Date());

        //按时间倒叙排序搜素记录
        Query query1 = Query.query(Criteria.where("userId").is(userId));
        query1.with(Sort.by(Sort.Direction.DESC, "createdTime"));
        List<ApUserSearch> apUserSearchList = mongoTemplate.find(query1, ApUserSearch.class);

        //不超过10条 直接添加
        if (apUserSearchList!=null&&apUserSearchList.size()<10) {
            mongoTemplate.save(apUserSearch);
        }else {
            //超过 替换最早的记录

            //先查询最早的一条记录
            ApUserSearch lastUserSearch = apUserSearchList.get(apUserSearchList.size() - 1);

            //查询并替换最新记录
            mongoTemplate.findAndReplace(Query.query(Criteria.where("id").is(lastUserSearch.getId())), apUserSearch);


        }


    }

}




































